/*
 * SvgC 1.0.2 - JavaScript SVG Library
 *
 * Copyright (c) 2010 Maciej Bielarz
 * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
 */
function SvgC (element, width, height) {
  if (typeof element == "string") {
    element = document.getElementById(element);
  }

  switch (element.tagName.toLowerCase()) {
    case "svg":
      this.svgEl    = element;
      this.svgDoc   = element.ownerDocument;
      this.root     = element;
      break;
    case "g":
    case "defs":
    case "clippath":
    case "marker":
    case "lineargradient":
    case "radialgradient":
      this.svgEl    = element.ownerSVGElement;
      this.svgDoc   = element.ownerSVGElement.ownerDocument;
      this.root     = element;
      this.attrs    = parent.attrs;
      break;
    case "div":
    case "span":
      this.svgEl    = element.ownerDocument.createElementNS(SvgC.svgNS, "svg");
      this.svgDoc   = element.ownerDocument;
      this.root     = this.svgEl;
      element.appendChild(this.svgEl);
      break;
    default: throw new Error("Unknown svg container: " + element.tagName);
  }

  this.attrs          = {};
  this.transform      = this.svgEl.createSVGMatrix();
  this.transform_flag = false;

  if (height != null) this.svgEl.setAttributeNS(null, "height", height);
  if (width  != null) this.svgEl.setAttributeNS(null, "width",  width);
}

SvgC.svgNS   = "http://www.w3.org/2000/svg";
SvgC.xlinkNS = "http://www.w3.org/1999/xlink";

SvgC.prototype.clone = function (parent) {
  var root = parent ? parent : this.root;
  var newC = new SvgC(parent);
  for (var k in this.attrs) {
    newC.attrs[k] = this.attrs[k];
  }
  newC.attrs["*"] = {};
  return newC;
}

SvgC.prototype.brush = function(name, attrs) {
  if (arguments.length == 1) {
    return this.brush("*", arguments[0]);
  }

  if (! (name in this.attrs)) this.attrs[name] = {};
  for (var k in attrs) {
    this.attrs[name][k] = attrs[k];
  }
}

SvgC.prototype.brushes = function() {
  for (var k in arguments[0]) {
    this.attrs[k] = arguments[0][k];
  }
}

SvgC.prototype.noBrush = function (brush) {
  return !(brush == null || typeof brush == 'string' && brush.search(/#|@|\./) == 0);
}

SvgC.prototype.bindNull = function (args) {
  return args.callee.apply(this, [null].concat(Array.prototype.slice.call(args, 0)));
}

SvgC.prototype.title = function (text) {
  var newEl =  this.svgDoc.createElementNS(SvgC.svgNS, "title");
  newEl.textContent = text;
  this.root.appendChild(newEl);
}

SvgC.prototype.desc = function (text) {
  var newEl =  this.svgDoc.createElementNS(SvgC.svgNS, "desc");
  newEl.innerText = text;
  this.root.appendChild(newEl);
}

SvgC.prototype.create  = function (name, brush, attrs_basic, attrs_other, append) {
  var newEl = this.svgDoc.createElementNS(SvgC.svgNS, name);
  var brArr = brush != null ? brush.split(",") : [];

  // all elements attributes
  for (var k in this.attrs["*"]) {
    newEl.setAttributeNS(null, k, this.attrs["*"][k]);
  }

  // this elemnet attributes
  for (var k in this.attrs[name]) {
    newEl.setAttributeNS(null, k, this.attrs[name][k]);
  }

  // brush attributes
  for (var i=0; i<brArr.length; i++) {
    var br = brArr[i];
    switch (br.charAt(0)) {
      case '#':
        newEl.setAttributeNS(null, "id", br.substr(1));
        for (var k in this.attrs[br]) {
          newEl.setAttributeNS(null, k, this.attrs[br][k]);
        }
        break;
      case '@':
        for (var k in this.attrs[br]) {
          newEl.setAttributeNS(null, k, this.attrs[br][k]);
        }
        break;
      case '.':
        newEl.setAttributeNS(null, "class", br.substr(1));
        break;
      default:
        throw new Error("Illegal brush description: " + br);
    }
  }

  // basic (required) attributes
  for (var k in attrs_basic) {
    newEl.setAttributeNS(null, k, attrs_basic[k]);
  }

  // additional attributes
  for (var k in attrs_other) {
    if (typeof attrs_other[k] == "function") {
      newEl.addEventListener(k, attrs_other[k], true);
    } else {
      newEl.setAttributeNS(null, k, attrs_other[k]);
    }
  }

  if (newEl.transform && this.transform_flag) {
    var trns = this.svgEl.createSVGTransform();
    trns.setMatrix(this.transform);
    newEl.transform.baseVal.appendItem(trns);
  }

  if (append == true) {
    this.root.appendChild(newEl);
  }

  return newEl;
}

SvgC.prototype.get = function(el, attr) {
  if (typeof el == "string") {
    el = this.svgEl.querySelector(el);
  }

  return attr != null ? el.getAttributeNS(null, attr) : el;
}

SvgC.prototype.set = function(el, attr, val) {
  if (typeof el == "string") {
    el = this.svgEl.querySelector(el);
  }

  if (attr == "textContent" && el.tagName.toLowerCase() == "text") {
    this.set_text(el, val);
  }

  return el.setAttributeNS(null, attr, val);
}

SvgC.prototype.set_text = function (textEl, text) {
  var textX = textEl.getAttributeNS(null, "x");
  var textH = textEl.currentStyle ?
              textEl.currentStyle["fontSize"] :
              this.svgDoc.defaultView.getComputedStyle(textEl, null).getPropertyValue("font-size");
  text = text.split("\n");

  while (textEl.hasChildNodes()) {
    textEl.removeChild(textEl.childNodes[0]);
  }

  for (var i=0; i<text.length; i++) {
    var spanEl = this.create("tspan", null, {dy:textH, x:textX}, {}, false);
    spanEl.appendChild(this.svgDoc.createTextNode(text[i]));
    textEl.appendChild(spanEl);
  }
}

SvgC.prototype.clientMatrix = function (element) {
  if (element == null) element = this.root;
  return element.getScreenCTM();
}

SvgC.prototype.rootMatrix = function (element) {
  if (element == null) element = this.root;

  var arr = [];
  while (element.tagName.toLowerCase() != "svg") {
    var list = element.transform.baseVal;
    for (var i=0; i<list.numberOfItems; i++) {
      arr.unshift(list.getItem(i).matrix);
    }
    element = element.parentNode;
  }

  var m    = this.svgEl.createSVGMatrix();
  for (var i=0; i<arr.length; i++) {
    m = m.multiply(arr[i]);
  }

  return m;
}

SvgC.prototype.from_client = function(x, y, element) {
  var p = this.svgEl.createSVGPoint();
  var m = this.clientMatrix(element).inverse();
  p.x   = x;
  p.y   = y;
  return p.matrixTransform(m);
}

SvgC.prototype.to_client = function(x, y, element) {
  var p = this.svgEl.createSVGPoint();
  var m = this.clientMatrix(element);
  p.x   = x;
  p.y   = y;
  return p.matrixTransform(m);
}

SvgC.prototype.to_root = function(x, y, element) {
  var p = this.svgEl.createSVGPoint();
  var m = this.rootMatrix(element);
  p.x   = x;
  p.y   = y;
  return p.matrixTransform(m);
}

SvgC.prototype.style = function(s_hash) {
  var s = this.create("style", null, {}, {}, true);
  var r = s.sheet;
  var style_str = "";

  for (var sel in s_hash) {
    style_str += sel + "{\n";
    for (var prop in s_hash[sel]) { 
      style_str += "\t" + prop + ":" + s_hash[sel][prop] + ";\n";
    }
    style_str += "}\n";
  }
  s.textContent = style_str;
}

SvgC.prototype.orgin = function() {
  this.transform = this.svgEl.createSVGMatrix();
  this.transform_flag = false;
  return this;
}

SvgC.prototype.setMatrix = function(m) {
  this.transform_flag = true;
  this.transform = m;
  return this;
}

SvgC.prototype.translate = function(dx, dy) {
  this.transform_flag = true;
  this.transform = this.transform.translate(dx, dy);
  return this;
}

SvgC.prototype.scale = function(sx, sy) {
  this.transform_flag = true;
  this.transform = arguments.length == 2 ?
    this.transform.scaleNonUniform(sx, sy) :
    this.transform.scale(sx);
  return this;
}

SvgC.prototype.rotate = function(angle, cx, cy) {
  this.transform_flag = true;
  this.transform = arguments.length == 3 ?
    this.transform.translate(cx, cy).rotate(angle).translate(-cx, -cy) :
    this.transform.rotate(angle);
  return this;
}

SvgC.prototype.skewX = function(angle) {
  this.transform_flag = true;
  this.transform = this.transform.skewX(angle);
  return this;
}

SvgC.prototype.skewY = function(angle) {
  this.transform_flag = true;
  this.transform = this.transform.skewY(angle);
  return this;
}

SvgC.prototype.flipX = function() {
  this.transform_flag = true;
  this.transform = this.transform.flipX();
  return this;
}

SvgC.prototype.flipY = function() {
  this.transform_flag = true;
  this.transform = this.transform.flipY();
  return this;
}

SvgC.prototype.rect = function(brush, x, y, w, h, attrs_ext) {
  if (this.noBrush(brush)) return this.bindNull(arguments);
  return this.create("rect", brush, {x:x, y:y, width:w, height:h}, attrs_ext, true);
}

SvgC.prototype.circle = function(brush, cx, cy, r, attrs_ext) {
  if (this.noBrush(brush)) return this.bindNull(arguments);
  return this.create("circle", brush, {cx:cx, cy:cy, r:r}, attrs_ext, true);
}

SvgC.prototype.ellipse = function(brush, cx, cy, rx, ry, attrs_ext) {
  if (this.noBrush(brush)) return this.bindNull(arguments);
  return this.create("ellipse", brush, {cx:cx, cy:cy, rx:rx, ry:ry}, attrs_ext, true);
}

SvgC.prototype.line = function(brush, x1, y1, x2, y2, attrs_ext) {
  if (this.noBrush(brush)) return this.bindNull(arguments);
  return this.create("line", brush, {x1:x1, y1:y1, x2:x2, y2:y2}, attrs_ext, true);
}

SvgC.prototype.polygon = function(brush, points, attrs_ext) {
  if (this.noBrush(brush)) return this.bindNull(arguments);
  if (typeof points == "object" && points instanceof Array) {points = points.join(" ");}
  return this.create("polygon", brush, {points:points}, attrs_ext, true);
}

SvgC.prototype.polyline = function(brush, points, attrs_ext) {
  if (this.noBrush(brush)) return this.bindNull(arguments);
  return this.create("polyline", brush, {points:points}, attrs_ext, true);
}

SvgC.prototype.path = function(brush, d, attrs_ext) {
  if (this.noBrush(brush)) return this.bindNull(arguments);
  if (typeof d == "object" && d instanceof Array) {d = d.join(" ");}
  return this.create("path", brush, {d:d}, attrs_ext, true);
}

SvgC.prototype.use = function(brush, x, y, link, attrs_ext) {
  if (this.noBrush(brush)) return this.bindNull(arguments);
  var u = this.create("use", brush, {x:x, y:y}, attrs_ext, true);
  u.setAttributeNS(SvgC.xlinkNS, "href", link);
  return u;
}

SvgC.prototype.image = function(brush, x, y, w, h, link, attrs_ext) {
  if (this.noBrush(brush)) return this.bindNull(arguments);
  var i = this.create("image", brush, {x:x, y:y, width:w, height:h}, attrs_ext, true);
  i.setAttributeNS(SvgC.xlinkNS, "href", link);
  return i;
}

SvgC.prototype.text = function(brush, x, y, text, attrs_ext) {
  if (this.noBrush(brush)) return this.bindNull(arguments);
  var textEl = this.create("text", brush, {x:x, y:y}, attrs_ext, false);
  this.root.appendChild(textEl);
  this.set_text(textEl, text);
  return textEl;
}

SvgC.prototype.gradient = function (brush, s, attrs_ext) {
  var reg_grL = /^l(\d+):(.*)/;
  var reg_grR = /^r(\d+%?)?(,(\d+%?))?(,(\d+%?))?:(.*)/;
  var reg_grS = /(\d+%?)-(#?[a-zA-Z0-9]+)(\/(\d+\.\d+))?/g
  var m, grad, rest;

  if (m = reg_grL.exec(s)) {
    var alfa = Number(m[1])*(Math.PI/180);
    var beta = Number(m[1])*(Math.PI/180) - Math.PI/2;
    var v    = [0, 0, 0, 0];
    if (m[1] <  45 && m[1] >=  0 ) {v = [0, 0, 1, Math.tan(alfa)];}
    if (m[1] <  90 && m[1] >=  45) {v = [0, 0, 1/Math.tan(alfa), 1];}
    if (m[1] < 135 && m[1] >=  90) {v = [1, 0, 1-Math.tan(beta), 1];}
    if (m[1] < 180 && m[1] >= 135) {v = [1, 0, 0, 1/Math.tan(beta)];}
    grad = this.create("linearGradient", brush, {x1:v[0], y1:v[1], x2:v[2], y2:v[3]}, attrs_ext, true);
    rest = m[2];
  }

  if (m = reg_grR.exec(s)) {
    grad = this.create("radialGradient", brush, {r:m[1]||'50%', cx:m[3]||'50%', cy:m[5]||'50%'}, attrs_ext, true);
    rest = m[6];
  }

  while (m = reg_grS.exec(rest)) {
    var attrs = {};
    attrs["offset"]       = m[1];
    attrs["stop-color"]   = m[2];
    attrs["stop-opacity"] = m[4] || 1;
    grad.appendChild(this.create("stop", null, attrs, {}, false));
  }
}

SvgC.prototype.marker = function (brush, x, y, w, h, fun, attrs_ext) {
  var m = this.create("marker", brush, {refX:x, refY:y, markerWidth:w, markerHeight:h}, attrs_ext, true);
  if (fun != null) fun(this.clone(m), m);
  return m;
}

SvgC.prototype.linearGradient = function (brush, x1, y1, x2, y2, fun, attrs_ext) {
  var g = this.create("linearGradient", brush, {x1:x1, y1:y1, x2:x2, y2:y2}, attrs_ext, true);
  if (fun != null) fun(this.clone(g), g);
  return g;
}

SvgC.prototype.radialGradient = function (brush, cx, cy, r, fun, attrs_ext) {
  var g = this.create("radialGradient", brush, {r:r, cx:cx, cy:cy}, attrs_ext, true);
  if (fun != null) fun(this.clone(g), g);
  return g;
}

SvgC.prototype.gradientStop = function (offset, color, attrs_ext) {
  return this.create("stop", null, {offset:offset, "stop-color":color}, attrs_ext, true);
}

SvgC.prototype.defs = function(fun) {
  var d = this.create("defs", null, {}, {}, true);
  if (fun != null) fun(this.clone(d), d);
  return d;
}

SvgC.prototype.g = function(brush, fun) {
  if (this.noBrush(brush)) return this.bindNull(arguments);
  var g = this.create("g", brush, {}, {}, true);
  if (fun != null) fun(this.clone(g), g);
  return g;
}

SvgC.prototype.clipPath = function(brush, fun) {
  var cp = this.create("clipPath", brush, {}, {}, true);
  if (fun != null) fun(this.clone(cp), cp);
  return cp;
}

SvgC.prototype.rgb = function (r, g, b) {
  return "rgb("+r+","+g+","+b+")";
}